
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "spi.h"
#include "gd32f30x.h"
#include "w25q32.h"
#include "message.h"
   
#define MAX_BLOCKSIZE         128    
#define MAX_SECTORSIZE        2048   

#define CMD_WRIRE_ENABLE      0x06
#define CMD_WRITE_DISABLE     0x04
#define CMD_READ_STATUS_R1    0x05
#define CMD_READ_STATUS_R2    0x35
#define CMD_READ_STATUS_R3    0x15
#define CMD_WRITE_STATUS_R    0x01 
#define CMD_PAGE_PROGRAM      0x02
#define CMD_QUAD_PAGE_PROGRAM 0x32 
#define CMD_BLOCK_ERASE64KB   0xd8
#define CMD_BLOCK_ERASE32KB   0x52
#define CMD_SECTOR_ERASE      0x20
#define CMD_CHIP_ERASE        0xC7
#define CMD_ERASE_SUPPEND     0x75 
#define CMD_ERASE_RESUME      0x7A 
#define CMD_POWER_DOWN        0xB9
#define CMD_HIGH_PERFORM_MODE 0xA3 
#define CMD_CNT_READ_MODE_RST 0xFF 
#define CMD_RELEASE_PDOWN_ID  0xAB 
#define CMD_MANUFACURER_ID    0x90
#define CMD_READ_UNIQUE_ID    0x4B
#define CMD_JEDEC_ID          0x9f

#define CMD_READ_DATA         0x03
#define CMD_FAST_READ         0x0B
#define CMD_READ_DUAL_OUTPUT  0x3B 
#define CMD_READ_DUAL_IO      0xBB 
#define CMD_READ_QUAD_OUTPUT  0x6B 
#define CMD_READ_QUAD_IO      0xEB 
#define CMD_WORD_READ         0xE3 
#define CMD_ENABLE_4_BYTE_ADDR    0xB7
#define CMD_EXIT_4_BYTE_ADDR      0xE9

#define SR1_BUSY_MASK		  0x01
#define SR1_WEN_MASK	      0x02


#define W25Q80 				  0XEF13 	
#define W25Q16 				  0XEF14
#define W25Q32 				  0XEF15
#define W25Q64 				  0XEF16
#define W25Q128				  0XEF17
#define W25Q256 			  0XEF18
#define W25QXX_TYPE 		  W25Q32

#if SPI_MODE == SPI_MASTER_MODE_POLL

static void spi_write_read_data(uint8_t *w_data,uint8_t *r_data,int len)
{
#if SPI_MODE == SPI_MASTER_MODE_POLL
	spi1_write_read_data(w_data,r_data,len);
#elif SPI_MODE == SPI_MASTER_MODE_DMA

#endif	
}

static void spc_dump(char *id,int rc, uint8_t *data,int len) 
{
    int i;
    printf("[%s] = %d\r\n",id,rc);
    for(i=0;i<len;i++) {
      printf("%0x ",data[i]);
      if ( (i % 10) == 9) printf("\r\n");
    }
    printf("\r\n");
}

static void w25q32_read_manufacturer_id(uint16_t *id)
{
    uint8_t tx_data[6] = {CMD_MANUFACURER_ID ,0,0,0,0,0};
    uint8_t rx_data[6] = {0};
    spi_write_read_data(tx_data,rx_data,6);
	*id = (rx_data[4]<<8)|rx_data[5];
}


static void w25q32_read_jedec_id(uint16_t *id)
{
    uint8_t tx_data[4] = {CMD_JEDEC_ID ,0,0,0};
    uint8_t rx_data[4] = {0};
    spi_write_read_data(tx_data,rx_data,4);
	*id = (rx_data[2]<<8)|rx_data[3];
}

static void w25q32_read_uniqie_id(void) 
{
    uint8_t tx_data[12] = {0x4B ,0,0,0};
    uint8_t rx_data[12] = {0};

    spi_write_read_data(tx_data,rx_data,12);
    print_register_value(rx_data,12);
}

/**
 * 状态寄存器1：
 *	BIT7  6   5   4   3   2   1   0
 *  SPR   RV  TB BP2 BP1 BP0 WEL BUSY
*/
static int w25q32_read_status_reg1(void)
{
	uint8_t tx_data[2];
	uint8_t rx_data[2]= {0};
  	tx_data[0] = CMD_READ_STATUS_R1;
  	tx_data[1] = 0xff;
  	spi_write_read_data (tx_data,rx_data,sizeof(rx_data));
	//spc_dump(__FUNCTION__,0,rx_data,sizeof(rx_data));
	return rx_data[1];
}

/**
 * 状态寄存器2：
 *BIT7  6   5   4   3   2   1   0
 *SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
*/
static int w25q32_read_status_reg2(void)
{
	uint8_t tx_data[2];
	uint8_t rx_data[2];
  	tx_data[0] = CMD_READ_STATUS_R2;
  	tx_data[1] = 0xff;
  	spi_write_read_data (tx_data,rx_data,sizeof(rx_data));
	//spc_dump(__FUNCTION__,0,rx_data,sizeof(rx_data));
	return rx_data[1];
}

static int w25q32_read_status_reg3(void)
{
	uint8_t tx_data[2];
	uint8_t rx_data[2];
  	tx_data[0] = CMD_READ_STATUS_R3;
  	tx_data[1] = 0xff;
  	spi_write_read_data (tx_data,rx_data,sizeof(rx_data));
	//spc_dump(__FUNCTION__,0,rx_data,sizeof(rx_data));
	return rx_data[1];
}

int w25q32_is_busy() 
{
  	uint8_t tx_data[2];
	uint8_t rx_data[2] = {0};

	tx_data[0] = CMD_READ_STATUS_R1;
	tx_data[1] = 0xff;
	spi_write_read_data (tx_data,rx_data,sizeof(rx_data));

	uint8_t r1;
	r1 = rx_data[1];
	if(r1 & SR1_BUSY_MASK)
		return 1;
	return 0;
}

//等待空闲
void w25q32_wait_busy(void)   
{   
	while((w25q32_read_status_reg1()&0x01)==0x01);           
} 

void w25q32_power_down(void) 
{
   	uint8_t tx_data;
	uint8_t rx_data;

  	tx_data = CMD_POWER_DOWN;
	spi_write_read_data (&tx_data,&rx_data,1);
}

void w25q32_write_enable(void) 
{
	uint8_t tx_data[1];
	uint8_t rx_data[1];
	tx_data[0] = CMD_WRIRE_ENABLE;
	spi_write_read_data(tx_data,rx_data,1);
}

void w25q32_write_disable(void) 
{
	uint8_t tx_data[1];
	uint8_t rx_data[1];
	tx_data[0] = CMD_WRITE_DISABLE;
	spi_write_read_data(tx_data,rx_data,1);
}

int w25q32_erase_all() 
{
  	uint8_t tx_data[1];
	uint8_t rx_data[1];
  	w25q32_write_enable();  
  	tx_data[0] = CMD_CHIP_ERASE;
	spi_write_read_data(tx_data,rx_data,1);

  	w25q32_wait_busy();
  	return 1;
}

void w25q32_erase_sector(uint32_t dst_addr)   
{  
   	uint8_t tx_data[4];
	uint8_t rx_data[4];

	uint32_t addr = dst_addr;
	addr<<=12;

	w25q32_write_enable();
	tx_data[0] = CMD_SECTOR_ERASE;
	tx_data[1] = (addr>>16) & 0xff;
	tx_data[2] = (addr>>8) & 0xff;
	tx_data[3] = addr & 0xff;
	spi_write_read_data (tx_data,rx_data,sizeof(rx_data));
	w25q32_wait_busy();
} 

uint16_t w25q32_read(uint32_t addr,uint8_t *buf,uint16_t n)
{ 
	uint8_t *tx_data;
	uint8_t *rx_data;

	tx_data = (uint8_t*)malloc(n+4);
	rx_data = (uint8_t*)malloc(n+4);

	tx_data[0] = CMD_READ_DATA;
	tx_data[1] = (addr>>16) & 0xFF;     // A23-A16
	tx_data[2] = (addr>>8) & 0xFF;      // A15-A08
	tx_data[3] = addr & 0xFF;           // A07-A00

	spi_write_read_data(tx_data,rx_data,n+4);

	memcpy(buf,&rx_data[4],n);

	free(tx_data);
	free(rx_data);

	return n;
}

uint16_t W25Q64_fastread(uint32_t addr,uint8_t *buf,uint16_t n) 
{
  	uint8_t *tx_data;
	uint8_t *rx_data;

	tx_data = (uint8_t*)malloc(n+5);
	rx_data = (uint8_t*)malloc(n+5);

	tx_data[0] = CMD_FAST_READ;
	tx_data[1] = (addr>>16) & 0xFF;     // A23-A16
	tx_data[2] = (addr>>8) & 0xFF;      // A15-A08
	tx_data[3] = addr & 0xFF;           // A07-A00
	tx_data[4] = 0;

	spi_write_read_data(tx_data,rx_data,n+5);

	memcpy(buf,&rx_data[5],n);
	
	free(tx_data);
	free(rx_data);

	return n;
}

uint16_t w25q32_page_write( uint16_t inaddr, uint8_t* buf, uint16_t n) 
{
  	uint8_t *tx_data;
	uint8_t *rx_data;

	tx_data = (uint8_t*)malloc(n+4);
	rx_data = (uint8_t*)malloc(n+4);

	w25q32_write_enable();  

	tx_data = (uint8_t*)malloc(n+4);
	tx_data[0] = CMD_PAGE_PROGRAM;
	tx_data[1] = (inaddr>>16) & 0xff;
	tx_data[2] = (inaddr>>8) & 0xff;
	tx_data[3] = inaddr & 0xFF;

	memcpy(&tx_data[4],buf,n);

	spi_write_read_data(tx_data,rx_data,n+4);

	while(w25q32_is_busy()) ;

	free(tx_data);
	free(rx_data);
	return 0;
}

void w25q32_write_no_check( uint16_t inaddr, uint8_t* buf, uint16_t n)
{
	uint16_t page_remain;
	page_remain = 256 - inaddr % 256;
	if (n <= page_remain)
		page_remain = n;
	while (1)
	{
		/* code */
		w25q32_page_write(inaddr,buf,page_remain);
		if(n == page_remain)
			break;
		else
		{
			buf += page_remain;
			inaddr += page_remain;

			n -= page_remain;
			if (n > 256)
				page_remain = 256;
			else
				page_remain = n;	
		}	
	}		
}

void w25q32_buffer_write(uint8_t *p_buf,uint16_t inaddr,uint16_t n)
{
	uint32_t sec_pos;
	uint16_t sec_off;
	uint16_t sec_remian;
	uint16_t i;
	uint8_t  w_buff[4096];

	sec_pos = inaddr / 4096 ;
	sec_off = inaddr % 4096;
	sec_remian = 4096 - sec_off;

	if (n <= sec_remian)
	{
		sec_remian = n;
	}

	while (1)
	{
		w25q32_read(sec_pos*4096,w_buff,4096);
		for(i=0;i<sec_remian;i++)
		{
			if (w_buff[sec_off + i] != 0xff)
			{
				break;
			}
		}
		if (i<sec_remian)
		{
			w25q32_erase_sector(sec_pos);
			for(int i=0;i<sec_remian;i++)
			{
				w_buff[i+sec_off] = p_buf[i];
			}
			w25q32_write_no_check(sec_pos*4096,w_buff,4096);
		}
		else
		{
			w25q32_write_no_check(inaddr,w_buff,4096);
		}
		if (n == sec_remian)
		{
			break;
		}
		else
		{
			sec_pos++;
			sec_off = 0;
			p_buf += sec_remian;
			inaddr += sec_remian;
			n -= sec_remian;
			if (n > 4096)
			{
				sec_remian = 4096;
			}
			else
			{
				sec_remian = n;
			}
		}
	}
}

void w25q32_init(void)
{
	uint16_t manufacturer_id = 0;
	uint16_t jedec_id = 0;

	w25q32_read_manufacturer_id(&manufacturer_id);
	w25q32_read_jedec_id(&jedec_id);
	if (manufacturer_id == W25QXX_TYPE)
	{
		printf("W25Q32 Init Succdess\r\n");
	}
}
#endif //SPI_SLAVE_MODE_INT